#include <iostream>
#include <vector>
#include <cassert>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "Task.hpp"
using namespace std;

int global_nums = 10;
Task global_t;

/*
    描述子进程信息和管道信息
*/
class EndPoint
{
public:
    pid_t _child_id;
    int _write_fd;
    EndPoint(pid_t id, int fd) : _child_id(id), _write_fd(fd)
    {
    }
};

/*
    子进程执行父进程发送的命令
*/
void execute_command()
{
    while (true)
    {
        int cmd = 0;
        int n = read(0, &cmd, sizeof(cmd));
        if (n == sizeof(cmd))
            global_t.Execute(cmd);
        else
            break;
    }
}

/*
    创建global_num个子进程，同时建立管道连接，
    将子进程信息和管道信息(EndPoint)添加到end_points里。
*/
void createProcess(vector<EndPoint> &end_points)
{
    vector<int> writeFds;
    for (int i = 0; i < global_nums; ++i)
    {
        // 创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        assert(n != -1);

        // 创建子进程
        pid_t id = fork();
        /***********子进程***************/
        assert(id != -1);
        if (id == 0)
        {
            close(pipefd[1]);
            for (auto &fd : writeFds)
                close(fd);

            // 输入重定向，本应从pipefd[0]读取的，现在从stdin读取
            dup2(pipefd[0], 0);
            // 子进程等待父进程的命令，只有父进程写了命令到管道里，才能执行
            // 因此如果父进程没写，子进程就会在这里阻塞着
            execute_command();

            close(pipefd[0]);
            exit(0);
        }
        /***********子进程***************/
        close(pipefd[0]);

        // 保存所有子进程信息和管道信息
        end_points.push_back(EndPoint(id, pipefd[1]));
        // 将pipefd[1]保存起来，让后续fork的子进程能够关闭它。
        writeFds.push_back(pipefd[1]);
    }
}

/*
    关闭并回收子进程
*/
void waitProcess(vector<EndPoint> &end_points)
{
    // 1.关闭管道的写端，意味着对应子进程也将会退出
    // 2.回收子进程，防止僵尸
    for (auto &ep : end_points)
    {
        close(ep._write_fd);
        waitpid(ep._child_id, nullptr, 0);
    }
}

/*
    父进程分配任务给子进程，    
    想怎么分配任务，自定义
*/
void distributeTasks(vector<EndPoint> &end_points)
{
    int k = 5;
    while (k--)
    {
        // TODO
        int cmd = COMMAND_MYSQL;                               // 选择任务
        int index = rand() % end_points.size();                // 选择子进程
        write(end_points[index]._write_fd, &cmd, sizeof(cmd)); // 向所选子进程的管道文件写入命令，供子进程读取

        sleep(1);
    }
}

int main()
{
    // 1.创建管道、子进程
    // 当前父进程的所有子进程信息、对应的管道信息都在这个vector里
    vector<EndPoint> end_points;
    createProcess(end_points);

    // 2.派发任务
    distributeTasks(end_points);

    // 3.关闭、回收子进程
    waitProcess(end_points);
    return 0;
}
